home *** CD-ROM | disk | FTP | other *** search
- /*
- File: StorageClassShim.c
-
- Contains: All functions for the Sample USB Storage Class Shim
-
- Version: 1.1
-
- Copyright: © 1998-1999 by Apple Computer, Inc., all rights reserved.
-
- */
-
- /*
- MacOS Headers
- */
- #include <CodeFragments.h>
- #include <Devices.h>
- #include <DriverServices.h>
- #include <MacTypes.h>
- #include <Processes.h>
- #include <Resources.h>
- #include <USB.h>
-
-
- /*
- Driver Headers
- */
- #include "StorageClassShim.h"
- #include "SampleStorageVersion.h"
- #include "SampleStorageDeviceID.h"
-
-
- /*
- Definitions, Enumerations and Typedefs used by the Shim
- */
- // Enumerations for the range of Unittable entries
- // in which InstallDriverFromMemory will try to install the driver
- enum
- {
- kUnitTableEntryStart = 48,
- kUnitTableEntryEnd = 127
- };
-
- // These are the structures and definitions used for the Refnum Association array.
- // This array handles associating USBDeviceRefs with UnitTable Driver refnums, so
- // Drivers can be removed from the UnitTable when the drive is removed.
- enum
- {
- kMaxNumberOfDrives = 50
- };
-
- struct RefnumAssociation
- {
- USBDeviceRef theDevRef;
- DriverRefNum drvrRefNum;
- };
-
- typedef struct RefnumAssociation RefnumAssociation;
-
-
- /*
- Global variables used by the Shim
- */
- static RefnumAssociation theRefAssoc[kMaxNumberOfDrives];
- static Boolean shimInFile;
- static FSSpec shimFSSpec;
- static TimerID gGiveTimeTimer = nil;
- static DriverRefNum gNewDrvrRef = 0;
- static USBDeviceNotificationParameterBlock gpb;
-
-
- /*
- Prototypes for internal support functions
- */
- static OSStatus GiveTimeSecondaryInterrupt( void *p1, void *p2);
- static OSStatus ShimOpenDriver(USBDeviceRef theDevRef);
- static OSStatus ShimCloseDriver(USBDeviceRef theDevRef);
-
-
- /*
- Exported Functions for the Code Fragment Manager
- */
- // This routine gets called by the Code Fragment Manager when the shim's code fragment is loaded
- OSErr CFragInitRoutine(CFragInitBlockPtr initBlkPtr)
- {
- shimInFile = false;
-
- if (CFragHasFileLocation(initBlkPtr->fragLocator.where))
- {
- shimInFile = true;
- shimFSSpec = *(initBlkPtr->fragLocator.u.onDisk.fileSpec); // save the FSSpec, in case we need it later
- }
- return noErr;
- }
-
-
- /*
- Functions Exported for the USB Expert
- */
- // This routine is exported for use by the USB Expert to initialize the shim when
- // the expert loads it
- OSStatus USBShim(void)
- {
- OSStatus status = noErr;
-
- // Clear out the Association array
- BlockZero((Ptr) theRefAssoc, sizeof(RefnumAssociation) * kMaxNumberOfDrives);
-
- // Setup notification for Vendor Specific Devices
- gpb.usbDeviceNotification = kNotifyAnyEvent; // tell me about everything
- gpb.usbClass = kUSBAnyClass;
- gpb.usbSubClass = kUSBAnySubClass;
- gpb.usbProtocol = kUSBAnyProtocol;
- gpb.usbVendor = kDriverVendorID; // Notify me of my specific Vendor ID
- gpb.usbProduct = kDriverProductID; // Notify me of my specific Product ID
- gpb.result = noErr;
- gpb.callback = (USBDeviceNotificationCallbackProcPtr) &myNotificationCallback;
- gpb.refcon = nil;
- USBInstallDeviceNotification (&gpb);
-
- return status;
- }
-
- // This routine is exported for use by the USB Expert to terminate the shim before it gets removed
- void USBShimTermination(void)
- {
- // Remove the device notofication
- USBRemoveDeviceNotification(gpb.token);
- }
-
- // This function is passed to the USB Expert for device add/remove notification
- void myNotificationCallback(USBDeviceNotificationParameterBlock *pb)
- {
- switch(pb->usbDeviceNotification) // why were we notified?
- {
- case kNotifyAddDevice: // because mass storage device appeared
- case kNotifyAddInterface: // because mass storage interface appeared
- {
- ShimOpenDriver(pb->usbDeviceRef);
- }
- break;
-
- case kNotifyRemoveDevice: // because a mass storage device or interface disappeared
- case kNotifyRemoveInterface: // because a mass storage device or interface disappeared
- {
- ShimCloseDriver(pb->usbDeviceRef);
- }
- break;
-
- default:
- break;
- }
- }
-
- /*
- Functions Internal to the Shim
- */
- // Function that will load and install the driver for a new device.
- // As stated in the USB DDK API, the notification always occurs at task time.
- OSStatus ShimOpenDriver(USBDeviceRef theDevRef)
- {
- OSStatus theErr = noErr;
- Handle hDrvrResource;
- short loopCount;
- THz currentZone;
-
- // We have been notified of a new device, check to see if we already
- // have installed a driver the USB Device Ref.
- for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
- {
- if(theDevRef == theRefAssoc[loopCount].theDevRef)
- {
- // Driver has already been installed for this USBDeviceRef,
- // Nothing else needs to be done, report back noErr to the notifier
- return noErr;
- }
- }
-
- // No driver has been loaded for this USBDeviceRef, find the driver's 'ndrv' resource
- if (shimInFile)
- {
- short oldResFileID = 0,
- myResFileID = 0;
-
- SetResLoad(true);
- oldResFileID = CurResFile(); // get the current resource file ID
- myResFileID = FSpOpenResFile(&shimFSSpec, fsRdPerm);
- UseResFile(myResFileID); // point at the shim file
-
- currentZone = GetZone ();
- SetZone ( SystemZone() );
- hDrvrResource = Get1Resource('ndrv', 128); // read in the driver from a ndrv resource
- DetachResource(hDrvrResource); // Detach the resource so it hangs around in the system heap
- SetZone (currentZone);
-
- UseResFile(oldResFileID); // point at the original file
- CloseResFile(myResFileID); // Make sure the resource file is closed
- }
- else
- {
- hDrvrResource = Get1Resource('ndrv', 128); // read in the driver from a ndrv resource
- }
-
- // We have found the driver's 'ndrv' resource, install it into the UnitTable
- if (hDrvrResource)
- {
- long drvrSize;
- Ptr pDrvrInMemory;
- DriverRefNum drvrRefNum;
- void *pTheStorageClassDispatchTable;
- CFragSymbolClass symClass;
- CFragConnectionID connID;
- short loopCount;
-
- // Lock the Driver Resource in memory
- HLock(hDrvrResource);
-
- // Get the resource information needed to install the Driver
- pDrvrInMemory = *hDrvrResource;
- drvrSize = GetHandleSize(hDrvrResource);
-
- // Install the driver
- theErr = InstallDriverFromMemory(pDrvrInMemory, drvrSize, nil, (RegEntryIDPtr) nil, kUnitTableEntryStart, kUnitTableEntryEnd, &drvrRefNum);
- if ( theErr != noErr )
- {
- // The driver could not be loaded, the shim will return the error and abort the driver load
- return theErr;
- }
-
- gNewDrvrRef = drvrRefNum;
-
- // Save the Driver refnum to remove the driver when the remove notification is recieved
- for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++ )
- {
- if (( theRefAssoc[loopCount].theDevRef == 0 ) && (theRefAssoc[loopCount].drvrRefNum == 0) )
- {
- theRefAssoc[loopCount].theDevRef = theDevRef;
- theRefAssoc[loopCount].drvrRefNum = drvrRefNum;
- break;
- }
- }
-
- // If the array is already full, this drive will not be added to the array. This means that the Shim will never
- // remove the driver it the drive is detached. This will only happen if more than kMaxNumberOfDrives are attached
- // at any one time.
-
- // Get the Storage class dispatch table
- USBGetDriverConnectionID(&theDevRef, &connID);
- currentZone = GetZone ();
- SetZone ( SystemZone() );
-
- theErr = FindSymbol(connID, "\pTheStorageClassDispatchTable", (Ptr *)&pTheStorageClassDispatchTable, &symClass);
- SetZone (currentZone);
-
- // If no error occured, pass the dispatch table pointer to the Driver
- if (theErr == noErr)
- {
- // Since we know that the Unit Table driver doesn't make any system calls
- // for this control code, we can make the call synchronously.
- theErr = Control(drvrRefNum, 500, &pTheStorageClassDispatchTable);
- }
-
- if ( theErr != noErr )
- {
- // if an error occurs, we should remove the driver from the unittable
- ShimCloseDriver(theDevRef);
- }
- }
-
- return theErr;
- }
-
- OSStatus ShimCloseDriver(USBDeviceRef theDevRef)
- {
- OSStatus theErr = noErr;
- short loopCount;
-
- for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
- {
- if(theDevRef == theRefAssoc[loopCount].theDevRef)
- {
- if(theRefAssoc[loopCount].drvrRefNum != 0)
- {
- VCBPtr vol;
-
- vol = (VCBPtr) (GetVCBQHdr())->qHead;
-
- while (vol)
- {
- // Check to see if this volume belongs to the driver
- // that is being removed
- if( vol->vcbDRefNum == theRefAssoc[loopCount].drvrRefNum)
- {
- IOParam offlinePB;
- OSErr err;
-
- BlockZero(&offlinePB, sizeof(IOParam));
- offlinePB.ioCompletion = nil;
- offlinePB.ioResult = noErr;
- offlinePB.ioNamePtr = nil;
- offlinePB.ioVRefNum = vol->vcbVRefNum;
-
- err = PBUnmountVol((ParamBlockRec *) &offlinePB);
- if(err == fBsyErr)
- {
- err = PBEject((ParamBlockRec *) &offlinePB);
- gNewDrvrRef = 0;
- }
- }
-
- vol = (VCBPtr) vol->qLink;
- }
-
- theErr = RemoveDriver(theRefAssoc[loopCount].drvrRefNum, false);
- theRefAssoc[loopCount].theDevRef = 0;
- theRefAssoc[loopCount].drvrRefNum = 0;
-
- if(gNewDrvrRef == 0)
- {
- OSStatus status;
- AbsoluteTime theWait;
-
- theWait = DurationToAbsolute(durationSecond);
- theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
- status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
- }
- }
-
- return theErr;
- }
- }
-
- return theErr;
- }
-
- // This is the interrupt timer to provide the USB Manager to get time if a device was
- // removed with a volume in use.
- // The shortcomings of this implementation are:
- // 1. This will only work if a single in use device is removed. Multiple devices can cause
- // problems where the shim could get about confused whether the device was reattached.
- // 2. This will only work if the device that is removed has only one busy volume mounted.
- // More than one volume may cause the shim to get confused about whether the volume was remounted
- OSStatus GiveTimeSecondaryInterrupt( void *p1, void *p2)
- {
- #pragma unused ( p1, p2 )
- Boolean DrvrVolumeFound = false;
-
- gGiveTimeTimer = nil;
-
- if(gNewDrvrRef != 0)
- {
- VCBPtr vol;
-
- vol = (VCBPtr) (GetVCBQHdr())->qHead;
-
- while (vol)
- {
- // Check to see if this volume belongs to the driver
- // that is being removed
- if( vol->vcbDRefNum == gNewDrvrRef)
- {
- DrvrVolumeFound = true;
- break;
- }
-
- vol = (VCBPtr) vol->qLink;
- }
- }
-
- if( DrvrVolumeFound == false )
- {
- OSStatus status;
- AbsoluteTime theWait;
-
- SystemTask();
- theWait = DurationToAbsolute(durationSecond);
- theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
- status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
- }
-
- return noErr;
- }
-